/**
 * Copyright (C), 2017, JieYi Software System Co., Ltd.
 * All rights reserved.
 * FileName:       MySlf4jLogFilter.java
 *
 * @Description: 重载日志输出过滤器，优化日志输出
 * @author: victor
 * @version: V1.0
 * Create Date:    2017年12月6日 下午3:10:08
 * <p>
 * Modification History:
 * Date         Author        Version        Discription
 * -------------------------------------------------------
 * 2017年12月6日      victor          1.0             add
 */
package com.fengwei.dao;

import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.proxy.jdbc.CallableStatementProxy;
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.SQLUtils.FormatOption;
import com.alibaba.druid.util.JdbcUtils;

import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * @ClassName: MySlf4jLogFilter
 * @Description:
 * @author: victor
 * @date: 2017年12月6日 下午3:10:08
 *
 */
public class MySlf4jLogFilter extends Slf4jLogFilter {
    private boolean statementPrepareAfterLogEnable = false;

    @Override
    protected void statementPrepareAfter(PreparedStatementProxy statement) {
        //对statementPrepareAfterLogEnable 与  statementCloseAfterLogEnabled 进行日志关闭
        if (statementPrepareAfterLogEnable && isStatementLogEnabled()) {
            statementLog("{conn-" + statement.getConnectionProxy().getId() + ", pstmt-" + statement.getId() + "} created. " + statement.getSql());
        }
        super.setStatementCloseAfterLogEnabled(false);
    }

    //将SQL 格式化成一行
    protected String formatSql2Line(String sql) {
        StringTokenizer whitespaceStripper = new StringTokenizer(sql);
        StringBuilder builder = new StringBuilder();
        for (; whitespaceStripper.hasMoreTokens(); builder.append(" ")) {
            builder.append(whitespaceStripper.nextToken());
        }
        sql = builder.toString();
        if (builder.indexOf("select ") != -1 && builder.indexOf(" from") != -1) {
            //对查询SQL进行精简处理
            sql = sql.substring(0, sql.indexOf("select ") + 6) + " *" + sql.substring(sql.indexOf(" from"), sql.length());
        }
        //去掉空格
        sql = sql.replaceAll(" = '", "='");
        return sql;
    }

    private void logExecutableSql(StatementProxy statement, String sql) {
        if (!isStatementExecutableSqlLogEnable()) {
            return;
        }

        int parametersSize = statement.getParametersSize();
        if (parametersSize == 0) {
            statementLog(sql);
            return;
        }

        List<Object> parameters = new ArrayList<Object>(parametersSize);
        for (int i = 0; i < parametersSize; ++i) {
            JdbcParameter jdbcParam = statement.getParameter(i);
            parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
        }

        String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
        String formattedSql = SQLUtils.format(sql, DbType.valueOf(dbType), parameters, new FormatOption(false, true));
        statementLog(formattedSql);
    }

    @Override
    protected void statement_executeErrorAfter(StatementProxy statement, String sql, Throwable error) {
        if (this.isStatementLogErrorEnabled()) {
            //将条件拼装进SQL日志
            //将parameter 依次替换sql中的条件‘?’
            sql = getSqlAndParam(statement, sql);

            if (!isStatementExecutableSqlLogEnable()) {
                statementLogError(sql, error);
            } else {
                int parametersSize = statement.getParametersSize();
                if (parametersSize > 0) {
                    List<Object> parameters = new ArrayList<Object>(parametersSize);
                    for (int i = 0; i < parametersSize; ++i) {
                        JdbcParameter jdbcParam = statement.getParameter(i);
                        parameters.add(jdbcParam != null ? jdbcParam.getValue() : null);
                    }
                    String dbType = statement.getConnectionProxy().getDirectDataSource().getDbType();
                    String formattedSql = SQLUtils.format(sql, DbType.valueOf(dbType), parameters, new FormatOption(false, true));
                    statementLogError(formattedSql, error);
                } else {
                    statementLogError(sql, error);
                }
            }
        }
    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean firstResult) {
        logExecutableSql(statement, sql);

        if (isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = statement.getLastExecuteTimeNano();
            double millis = nanos / (1000 * 1000);

            //将条件拼装进SQL日志
            //将parameter 依次替换sql中的条件‘?’
            sql = getSqlAndParam(statement, sql);

            statementLog(String.format("%.3f", millis) + "ms " + sql);
        }
    }

    @Override
    protected void statementExecuteQueryAfter(StatementProxy statement, String sql, ResultSetProxy resultSet) {
        logExecutableSql(statement, sql);

        if (isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = statement.getLastExecuteTimeNano();
            double millis = nanos / (1000 * 1000);

            //将条件拼装进SQL日志
            //将parameter 依次替换sql中的条件‘?’
            sql = getSqlAndParam(statement, sql);

            statementLog(String.format("%.3f", millis) + "ms " + sql);
        }
    }

    @Override
    protected void statementExecuteUpdateAfter(StatementProxy statement, String sql, int updateCount) {
        logExecutableSql(statement, sql);

        if (isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = statement.getLastExecuteTimeNano();
            double millis = nanos / (1000 * 1000);

            //将条件拼装进SQL日志
            //将parameter 依次替换sql中的条件‘?’
            sql = getSqlAndParam(statement, sql);

            statementLog(String.format("%.3f", millis) + "ms " + sql);
        }
    }

    @Override
    protected void statementExecuteBatchAfter(StatementProxy statement, int[] result) {
        String sql;
        if (statement instanceof PreparedStatementProxy) {
            sql = ((PreparedStatementProxy) statement).getSql();
        } else {
            sql = statement.getBatchSql();
        }

        logExecutableSql(statement, sql);

        if (isStatementLogEnabled()) {
            statement.setLastExecuteTimeNano();
            double nanos = statement.getLastExecuteTimeNano();
            double millis = nanos / (1000 * 1000);

            //将条件拼装进SQL日志
            //将parameter 依次替换sql中的条件‘?’
            sql = getSqlAndParam(statement, sql);

            statementLog(String.format("%.3f", millis) + "ms " + sql);
        }
    }

    @Override
    public void connection_setAutoCommit(FilterChain chain, ConnectionProxy connection, boolean autoCommit) throws SQLException {
        chain.connection_setAutoCommit(connection, autoCommit);
    }

    //将条件拼装进SQL日志
    //将parameter 依次替换sql中的条件?
    private String getSqlAndParam(StatementProxy statement, String sql) {
        if (statement.getParametersSize() == 0) {
            return formatSql2Line(sql);
        }

        List<String> paramList = new ArrayList<String>();
        for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
            JdbcParameter parameter = statement.getParameter(i);
            if (parameter == null) {
                continue;
            }

            int sqlType = parameter.getSqlType();
            Object value = parameter.getValue();
            switch (sqlType) {
                case Types.NULL:
                    paramList.add("NULL");
                    break;
                default:
                    paramList.add(String.valueOf(value));
                    break;
            }
        }

        for (String param : paramList) {
            sql = sql.replaceFirst("[?]", "\'" + param + "\'");
        }

        return formatSql2Line(sql);
    }

    private String stmtId(StatementProxy statement) {
        StringBuffer buf = new StringBuffer();
        if (statement instanceof CallableStatementProxy) {
            buf.append("cstmt-");
        } else if (statement instanceof PreparedStatementProxy) {
            buf.append("pstmt-");
        } else {
            buf.append("stmt-");
        }
        buf.append(statement.getId());

        return buf.toString();
    }

    @Override
    protected void logParameter(PreparedStatementProxy statement) {

        if (false) {
            //参数和参数类型不打印。直接在SQL中拼接出来
            if (statement.getParametersSize() > 0) {
                {
                    StringBuffer buf = new StringBuffer();
                    buf.append("{conn-");
                    buf.append(statement.getConnectionProxy().getId());
                    buf.append(",");
                    buf.append(stmtId(statement));
                    buf.append("}");
                    buf.append(" Parameters:[");

                    for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
                        JdbcParameter parameter = statement.getParameter(i);
                        if (i != 0) {
                            buf.append(",");
                        }
                        if (parameter == null) {
                            continue;
                        }

                        int sqlType = parameter.getSqlType();
                        Object value = parameter.getValue();
                        switch (sqlType) {
                            case Types.NULL:
                                buf.append("NULL");
                                break;
                            default:
                                buf.append(String.valueOf(value));
                                break;
                        }
                    }
                    buf.append("]");
                    statementLog(buf.toString());
                }

                {
                    //默认SQL 类型不输出
                    StringBuffer buf = new StringBuffer();
                    buf.append("{conn-");
                    buf.append(statement.getConnectionProxy().getId());
                    buf.append(",");
                    buf.append(stmtId(statement));
                    buf.append("}");
                    buf.append(" Types:[");
                    for (int i = 0, parametersSize = statement.getParametersSize(); i < parametersSize; ++i) {
                        JdbcParameter parameter = statement.getParameter(i);
                        if (i != 0) {
                            buf.append(",");
                        }
                        if (parameter == null) {
                            continue;
                        }
                        int sqlType = parameter.getSqlType();
                        buf.append(JdbcUtils.getTypeName(sqlType));
                    }
                    buf.append("]");
                    statementLog(buf.toString());
                }
            }
        }
    }

}
